/**
 * @file lwip_user_init.c
 * @author zdk
 * @brief 初始化lwip协议栈
 * @version 0.1
 * @date 2021-07-10
 * 
 * BSD 3-Clause License
 * 
 * Copyright (c) 2021, water_zhang
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * 
 * * Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 */

#include "lwip_user_init.h"

#include "stm32f4xx.h"
#include "lwip/sys.h"
#include "lwip/init.h"
#include "lwip/netif.h"
#include "lwip/timeouts.h"
#include "lwip/tcpip.h"

#include "bsp_print.h"
#include "bsp_ethernet.h"
#include "eth0.h"

#include "bsp_led.h"

#include "rtthread.h"

static rt_sem_t sem_recvd_packet; /* 用于网卡中断与数据包接收线程之间的同步 */
static struct netif eth0_netif;

/**
 * @brief 网卡数据包接收中断回调
 * 
 * @param param 
 */
static void Lwip_User_IRQ_Callback(void *param)
{
    rt_sem_t *sem_recvd;

    sem_recvd = (rt_sem_t *)param; /* 获取传入的信号量句柄 */

    LWIP_ASSERT("obj != NULL", sem_recvd);

    /* 释放信号量通知数据包已接收 */
    rt_sem_release(*sem_recvd);

    /* 翻转调试用LED */
    Bsp_LED_Toggle(LED_ORANGE);
}

/**
 * @brief 查询是否收到以太网报文，收到后传送给LwIP协议栈处理
 * @return uint16_t 返回本次处理的报文数量
 * 
 */
static uint16_t Lwip_User_PollEthernetPacket(void)
{
    uint16_t cnt; /* 记录本次处理了多少数据包 */
    err_t ret = ERR_OK;

    /* 处理完所有数据包直到没有数据包待处理 */
    cnt = 0;
    do
    {
        ret = eth0_input(&eth0_netif);
        if (ERR_OK == ret)
        {
            cnt++;
        }
    } while (ERR_OK == ret);
    return cnt;
}

/**
 * @brief 网络处理线程
 * 
 */
void Lwip_User_Process(void *param)
{
    uint16_t cnt;
    rt_sem_t sem_recvd;
    sem_recvd = (rt_sem_t)param; /* 获取传入的信号量句柄 */

    while (1)
    {
        /* 等待直到收到网卡中断发出的信号量 */
        if (RT_EOK == rt_sem_take(sem_recvd, RT_WAITING_FOREVER))
        {
            /* 信号量由中断发出，获取触发网卡中断的标志位 */
            uint8_t irq;
            irq = Bsp_Ethernet_GetInterruptFlag();
            LWIP_DEBUGF(NETIF_DEBUG, ("Lwip_User_Process: ethernet irq is %02X.\r\n", irq));

            /* 判断数据包待处理标志位是否置位 */
            if (ETH_FLAG_PKT & irq)
            {
                /* 查询并处理以太网报文 */
                cnt = Lwip_User_PollEthernetPacket();
                /* 打印处理了多少数据包 */
                LWIP_DEBUGF(NETIF_DEBUG, ("Lwip_User_Process: %u ethernet packet processes.\r\n", cnt));
            }
        }
    }
}

/**
 * @brief 网络初始化
 * 
 */
int Lwip_User_Init(void)
{
    ip4_addr_t ip4_addr, ip4_netmask, ip4_gw;
    rt_thread_t thread_eth_recv;

    /* 初始化协议栈 */
    tcpip_init(NULL, NULL);

    /* 添加网卡 */
    IP4_ADDR(&ip4_addr, 192, 168, 31, 43);    /* 设置IP地址 */
    IP4_ADDR(&ip4_netmask, 255, 255, 255, 0); /* 设置子网掩码 */
    IP4_ADDR(&ip4_gw, 192, 168, 31, 1);       /* 设置网关地址 */
    if (!netif_add(&eth0_netif, &ip4_addr, &ip4_netmask, &ip4_gw, NULL, eth0_init, tcpip_input))
    {
        LWIP_DEBUGF(NETIF_DEBUG, ("Lwip_User_Init: eth0 netif add failure.\n"));
    }
    netif_set_default(&eth0_netif);
    netif_set_up(&eth0_netif);

    /* 初始化信号量，用于收到数据包时触发数据包处理 */
    sem_recvd_packet = rt_sem_create("mac_recvd", 0, RT_IPC_FLAG_PRIO);
    if (!sem_recvd_packet)
    {
        LWIP_DEBUGF(NETIF_DEBUG, ("Lwip_User_Init: sem_recvd_packet create failure.\r\n"));
    }

    /* 注册数据包接收中断处理回调 */
    Bsp_Ethernet_RegisterRecvCallback(Lwip_User_IRQ_Callback, (void *)&sem_recvd_packet);

    /* 使能网卡中断 */
    Bsp_Ethernet_EnableINT(ETH_FLAG_PKT | ETH_FLAG_RXER | ETH_FLAG_TX | ETH_FLAG_TXER);

    /* 创建以太网报文接收处理线程并启动 */
    thread_eth_recv = rt_thread_create("eth_recv", Lwip_User_Process, (void *)sem_recvd_packet, 1024, 2, 10);
    rt_thread_startup(thread_eth_recv);

    return RT_EOK;
}
INIT_ENV_EXPORT(Lwip_User_Init);

#ifdef FINSH_USING_MSH

/* 注册网卡相关命令行命令 */

/**
 * @brief 显示网卡寄存器当前值
 * 
 * @return long 恒返回0
 */
long eth_reg(void)
{
    eth_info info;
    if(ETH_OK == Bsp_Ethernet_GetInfo(&info))
    {
        rt_kprintf("enc28j60 register \r\n");
        rt_kprintf("ctr :       EIE=%02XH       EIR=%02XH     ESTAT=%02XH    ECON1=%02XH    ECON1=%02XH\r\n", 
        info.nobank.EIE, info.nobank.EIR, info.nobank.ESTAT, info.nobank.ECON1, info.nobank.ECON2);
        rt_kprintf("mac :    MACON1=%02XH    MACON2=%02XH    MACON3=%02XH   MACON4=%02XH\r\n",
        info.bank2.MACON1, info.bank2.MACON2, info.bank2.MACON3, info.bank2.MACON4);
        rt_kprintf("other : ERXFCON=%02XH   EPKTCNT=%02XH\r\n", info.bank1.ERXFCON, info.bank1.EPKTCNT);
    }
    return 0;
}
FINSH_FUNCTION_EXPORT(eth_reg, show enc28j60 registers);
MSH_CMD_EXPORT(eth_reg, show enc28j60 registers);

#endif /* FINSH_USING_MSH */
